﻿using ArcSoftFace.Entity;
using ArcSoftFace.SDKModels;
using ArcSoftFace.SDKUtil;
using ArcSoftFace.Utils;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Text;

namespace ArcSoftFaceTool.Utils
{
    /// <summary>
    /// 人脸检测工具类
    /// </summary>
    public class FaceDetectionUtil : IDisposable
    {
        private static object thisLock = new object();
        private static FaceDetectionUtil thisInstance;

        public static string AppId = "";
        public static string SDKKey = "";
        private bool disposedValue;

        /// <summary>
        /// 获取一个全局唯一实例
        /// </summary>
        /// <returns></returns>
        public static FaceDetectionUtil GetInstance()
        {
            if (thisInstance != null)
            {
                return thisInstance;
            }
            lock (thisLock)
            {
                if (thisInstance != null)
                {
                    return thisInstance;
                }
                thisInstance = new FaceDetectionUtil();
            }
            return thisInstance;
        }

        /// <summary>
        /// 创建一个人脸识别工具类
        /// </summary>
        public FaceDetectionUtil()
        {
            if (string.IsNullOrEmpty(AppId) || string.IsNullOrEmpty(SDKKey))
            {
                throw new Exception("AppId or SDKKey is null");
            }
        }
        #region 参数定义
        /// <summary>
        /// 引擎Handle
        /// </summary>
        private IntPtr pImageEngine = IntPtr.Zero;

        #endregion


        /// <summary>
        /// 初始化引擎
        /// </summary>
        public void InitEngines()
        {
            if(pImageEngine !=  IntPtr.Zero)
            {
                return;
            }

            //读取配置文件
            string appId = FaceDetectionUtil.AppId;
            string sdkKey64 = FaceDetectionUtil.SDKKey;


            //在线激活引擎    如出现错误，1.请先确认从官网下载的sdk库已放到对应的bin中，2.当前选择的CPU为x86或者x64
            int retCode = 0;
            try
            {
                retCode = ASFFunctions.ASFActivation(appId, sdkKey64);
                
            }
            catch (Exception ex)
            {
                if (ex.Message.Contains("无法加载 DLL"))
                {
                    throw new Exception("请将sdk相关DLL放入bin对应的x86或x64下的文件夹中!");
                }
                else
                {
                    throw new Exception($"{AppDomain.CurrentDomain.BaseDirectory} {Environment.NewLine}激活引擎失败! {Environment.NewLine} {ex.ToString()}");
                }
            }
            if (!(retCode == 90114 || retCode == 0))
            {
                throw new Exception($"激活引擎失败! 错误代码：{retCode}");
            }

            //Console.WriteLine("Activate Result:" + retCode);

            //初始化引擎
            uint detectMode = DetectionMode.ASF_DETECT_MODE_IMAGE;
            //Image模式下检测脸部的角度优先值
            int imageDetectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_ONLY;
            //人脸在图片中所占比例，如果需要调整检测人脸尺寸请修改此值，有效数值为2-32
            int detectFaceScaleVal = 16;
            //最大需要检测的人脸个数
            int detectFaceMaxNum = 2;
            //引擎初始化时需要初始化的检测功能组合
            int combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_AGE | FaceEngineMask.ASF_GENDER | FaceEngineMask.ASF_FACE3DANGLE;
            //初始化引擎，正常值为0，其他返回值请参考http://ai.arcsoft.com.cn/bbs/forum.php?mod=viewthread&tid=19&_dsign=dbad527e
            retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal,
                detectFaceMaxNum, combinedMask, ref pImageEngine);
            //Console.WriteLine("pImageEngine InitEngine Result:" + retCode);
            //Console.WriteLine((retCode == 0) ? "引擎初始化成功!\n" : string.Format("引擎初始化失败!错误码为:{0}\n", retCode));

        }

        private void CheckEngines()
        {
            if (pImageEngine == IntPtr.Zero)
            {
                lock(this)
                {
                    InitEngines();
                }
                
            }
        }

        /// <summary>
        /// 销毁引擎
        /// </summary>
        public void UninitEngine()
        {
            if (pImageEngine == IntPtr.Zero)
            {
                return;
            }

            //销毁引擎
            int retCode = ASFFunctions.ASFUninitEngine(pImageEngine);
            //Console.WriteLine("UninitEngine pImageEngine Result:" + retCode);
            pImageEngine = IntPtr.Zero;
        }

        /// <summary>
        /// 人脸检测入口函数
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="multiFaceInfo">返回人脸检测结果</param>
        public void FaceDetection(Bitmap bmp,out ASF_MultiFaceInfo multiFaceInfo)
        {
            CheckEngines();

            Bitmap bmpFace = bmp;
            bool isNewBmp = false;
            if (bmp.Width > 480 || bmp.Height > 640 || bmp.Width % 4 != 0)
            {
                bmpFace = (Bitmap)ImageUtil.ScaleImage(bmp, 480, 640);
                //bmp.Dispose();
                isNewBmp = true;
            }
            bmp = null;

            multiFaceInfo = new ASF_MultiFaceInfo();

            BitmapData data = bmpFace.LockBits(new Rectangle(0, 0, bmpFace.Width, bmpFace.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            try
            {
                //位图中第一个像素数据的地址。它也可以看成是位图中的第一个扫描行
                IntPtr ptr = data.Scan0;

                ImageInfo imageInfo = new ImageInfo();

                //填充引用对象字段值
                imageInfo.width = data.Width;
                imageInfo.height = data.Height;
                imageInfo.format = (int)ASF_ImagePixelFormat.ASVL_PAF_RGB24_B8G8R8;

                imageInfo.imgData = ptr;
                lock (this)
                {
                    multiFaceInfo = FaceUtil.DetectFace(pImageEngine, imageInfo);
                }
                
                imageInfo = null;
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                bmpFace.UnlockBits(data);
                if(isNewBmp)
                {
                    bmpFace.Dispose();
                    bmpFace = null;
                }
            }
            data = null;
        }

        /// <summary>
        /// 根据图片的Base64编码检测图片中的人脸包含情况
        /// </summary>
        /// <param name="sImageBase64"></param>
        /// <param name="multiFaceInfo"></param>
        public void FaceDetection(string sImageBase64 , out ASF_MultiFaceInfo multiFaceInfo)
        {
            CheckEngines();
            string strbase64 = sImageBase64.Substring(sImageBase64.IndexOf(',') + 1);
            strbase64 = strbase64.Trim('\0');
            byte[] arr = Convert.FromBase64String(strbase64);
            using (MemoryStream ms = new MemoryStream(arr))
            {
                using(Bitmap bmp = new Bitmap(ms))
                {
                    FaceDetection(bmp, out multiFaceInfo);
                }
            }
            
        }


        #region Dispose

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)
                }

                // TODO: 释放未托管的资源(未托管的对象)并重写终结器
                // TODO: 将大型字段设置为 null

                UninitEngine();

                disposedValue = true;
            }
        }

        // // TODO: 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器
        ~FaceDetectionUtil()
        {
            // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
            Dispose(disposing: false);
        }

        public void Dispose()
        {
            // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }
}
